package server.world;

import general.datastructures.PriorityQueue;
import general.datastructures.Vector2f;
import general.datastructures.Waypoint;
import general.exceptions.DestinationNotPassableException;
import general.exceptions.Exception;
import general.exceptions.IllegalPlayerCountException;
import general.exceptions.NoRouteFoundException;
import general.exceptions.OutOfMapException;
import general.interfaces.IUnit;

import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Vector;



import org.lwjgl.BufferUtils;

import client.ogl.Texture;
import client.ogl.TextureLoader;



/**
 * Class which describes a Map, including different terrain types
 * Prepared to be drawn with OGL
 * 
 * @author Tim
 * @version 0.5.0
 * @since 0.4.0
 * 
 */
public class OldMap {
	public static final int TERRAIN_WATER = -1;
	public static final int TERRAIN_GRASS = -2;
	public static final int TERRAIN_MUD = -3;
	public static final int TERRAIN_ROCK = -4;
	public static final int TERRAIN_FORREST = -5;

	/**
	 * TemplateID for a very small debug Map:
	 * Size: 10x10
	 * Terrain: Grass, a little area of forrest in the middle
	 * Spawnpoints: None
	 */
	public static final int TEMPLATE_DEBUG = 101;

	/**
	 * TemplateID for a small and simple Map:
	 * Size: 30x30
	 * Terrain: Grass, an area of forrest in the middle, surrounded by water
	 * Spawnpoints: p1: (10|10), p2: (20|20), p3: (10|20), p4: (20|10)
	 */
	public static final int TEMPLATE_ISLAND = 102;

	/**
	 * TemplateID for a small and simple Map:
	 * Size: 30x30
	 * Terrain: Grass, an area of forrest in the middle, surrounded by water
	 * Spawnpoints: p1: (10|10), p2: (20|20), p3: (10|20), p4: (20|10)
	 * 
	 * @deprecated
	 */
	public static final int TEMPLATE_ISLAND_CONSOLE = 103;
	
	/**
	 * Creates Maps, based of Template ID's	 * 
	 * 
	 * @param ID Template ID
	 * @param playercount numbers of players (2-4)
	 * @return the created map
	 * @throws IllegalPlayerCountException Is thrown, if the passed number of players is not supported by the chosen map
	 * @throws IOException An IOException is thrown, when any Map-Texture can not be loaded
	 */
	public static OldMap createTemplateMap(int ID, int playercount) throws IllegalPlayerCountException, IOException
	{
		switch (ID)
		{

		case TEMPLATE_DEBUG:
			byte[][] data1 = new byte[10][10];
			for (int i=0; i<10; i++)
				for(int j=0; j<10; j++)
				{
					if (i>3&&i<6&&j>3&&j<6)
					{
						data1[i][j] = OldMap.TERRAIN_FORREST;
					}
					else
					{
						data1[i][j] = OldMap.TERRAIN_GRASS;
					}
				}
			return new OldMap(data1,10,10,null,32);

		case TEMPLATE_ISLAND_CONSOLE:
			byte[][] data2 = new byte[30][30];

			Vector<Vector2f> mc_spawnpoints2 = new Vector<Vector2f>();


			if (playercount > 4 || playercount < 2) {
				throw new IllegalPlayerCountException("Ungueltige Spieleranzahl!", playercount);
			}
			mc_spawnpoints2.add(new Vector2f(10, 10));
			mc_spawnpoints2.add(new Vector2f(20,20));
			if (playercount >=3) {
				mc_spawnpoints2.add(new Vector2f(10,20));

				if (playercount == 4) {
					mc_spawnpoints2.add(new Vector2f(20,10));
				}
			}

			//map fuellen
			for (int i=0; i<10000; i++)
				for(int j=0; j<10000; j++)
				{
					if(i<5||i>=25||j<5||j>=25)
					{
						data2[i][j] = OldMap.TERRAIN_WATER;
					}
					else if (i>13&&i<18&&j>13&&j<18)
					{
						data2[i][j] = OldMap.TERRAIN_FORREST;
					}
					else
					{
						data2[i][j] = OldMap.TERRAIN_GRASS;
					}
				}

			return new OldMap(data2,30,30,mc_spawnpoints2);
			

		case TEMPLATE_ISLAND:
		default:
			byte[][] data = new byte[10000][10000];

			Vector<Vector2f> mc_spawnpoints = new Vector<Vector2f>();


			if (playercount > 4 || playercount < 2) {
				throw new IllegalPlayerCountException("Ungueltige Spieleranzahl!", playercount);
			}
			mc_spawnpoints.add(new Vector2f(10, 10));
			mc_spawnpoints.add(new Vector2f(20,20));
			if (playercount >=3) {
				mc_spawnpoints.add(new Vector2f(10,20));

				if (playercount == 4) {
					mc_spawnpoints.add(new Vector2f(20,10));
				}
			}

			//map fuellen
			for (int i=0; i<10000; i++)
				for(int j=0; j<10000; j++)
				{
					if(i<5||i>=25||j<5||j>=25)
					{
						data[i][j] = OldMap.TERRAIN_WATER;
					}
					else if (i>13&&i<18&&j>13&&j<18)
					{
						data[i][j] = OldMap.TERRAIN_FORREST;
					}
					else
					{
						data[i][j] = OldMap.TERRAIN_GRASS;
					}
				}

			return new OldMap(data,10000,10000,mc_spawnpoints,32);
		}
	}

	private int width;
	private int height;
	private byte data[][];
	
	//TODO entfernen, wenn fabriken zur einheitenerzeugung existieren
	private Vector<Vector2f> mc_spawnpoints;
	private int tilesize;
	
	private FloatBuffer verts_buf = BufferUtils.createFloatBuffer(2*4);
	private FloatBuffer tex_buf = BufferUtils.createFloatBuffer(2*4);
	
	private HashMap<Integer, Texture> textures = new HashMap<Integer, Texture>();
	
	/**
	 * private constructor, which shall only be used by the createTemplateMap function
	 * This version is for a non-graphical visualisation, so it has no Tielsize and does not try to load Textures
	 * 
	 * @param data
	 * @param width
	 * @param height
	 * @param spawnpoints
	 * @throws IOException 
	 */
	private OldMap(byte data[][], int width, int height, Vector<Vector2f> mc_spawnpoints) throws IOException
	{
		this.width = width;
		this.height = height;
		this.data = data;
		this.mc_spawnpoints = mc_spawnpoints;
	}
	
	/**
	 * private constructor, which shall only be used by the createTemplateMap function
	 * 
	 * @param data
	 * @param width
	 * @param height
	 * @param spawnpoints
	 * @throws IOException 
	 */
	private OldMap(byte data[][], int width, int height, Vector<Vector2f> mc_spawnpoints, int tilesize) throws IOException
	{
		this.width = width;
		this.height = height;
		this.data = data;
		this.mc_spawnpoints = mc_spawnpoints;
		this.tilesize = tilesize;
		
		verts_buf.put(new float[]{
				0.0f, 0.0f,
				tilesize, 0.0f,
				tilesize, tilesize,
				0.0f, tilesize
		});
		
		tex_buf.put(new float[]{
				0.0f,0.0f,
				1.0f,0.0f,
				1.0f,1.0f,
				0.0f,1.0f

		});
		
		textures.put(OldMap.TERRAIN_GRASS, TextureLoader.getTexture("grass.png"));
		textures.put(OldMap.TERRAIN_FORREST, TextureLoader.getTexture("forrest.png"));
		textures.put(OldMap.TERRAIN_ROCK, TextureLoader.getTexture("rock.png"));
		textures.put(OldMap.TERRAIN_MUD, TextureLoader.getTexture("mudd.png"));
		textures.put(OldMap.TERRAIN_WATER, TextureLoader.getTexture("water.png"));		
	}

	/**
	 * 
	 * @param x X-Coordinate
	 * @param y Y-Coordinate
	 * @return Terraintype at the passed coordinates
	 */
	public int getData(int x, int y){
		return data[x][y];
	}

	/**
	 * 
	 * @param vehicletype Vehicletype @see {@link IUnit}
	 * @param p {@link Vector2f}
	 * @return True, if the passed vehicletype can pass the passed {@link Vector2f}, otherwise false
	 */
	public boolean isPassable(int vehicletype, Vector2f mc_p)
	{
		if(
				((vehicletype == IUnit.TYPE_AMPHIBIOUS || vehicletype == IUnit.TYPE_LAND) && (data[(int)mc_p.x()][(int)mc_p.y()] == OldMap.TERRAIN_GRASS || data[(int) mc_p.x()][(int) mc_p.y()] == OldMap.TERRAIN_MUD )) ||
				((vehicletype == IUnit.TYPE_AMPHIBIOUS || vehicletype == IUnit.TYPE_WATER) && (data[(int)mc_p.x()][(int)mc_p.y()] == OldMap.TERRAIN_WATER)) ||
				(vehicletype == IUnit.TYPE_AIR)
			)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	/**
	 * 
	 * @return returns the Width of the map
	 */
	public int getWidth(){
		return width;
	}

	/**
	 * 
	 * @return returns the Height of the map
	 */
	public int getHeight(){
		return height;
	}

	/**
	 * 
	 * @return returns the tilesize of the map
	 */
	public int getTilesize()
	{
		return tilesize;
	}
	
	public FloatBuffer getVertexBuffer()
	{
		this.verts_buf.rewind();
		return this.verts_buf;
	}
	
	public FloatBuffer getTextureBuffer()
	{
		this.tex_buf.rewind();
		return this.tex_buf;
	}
	
	public void BindMapTexture(int terraintype)
	{
		textures.get(terraintype).bind();
	}
	
	/**
	 * 
	 * @param PlayerID
	 * @return Returns the Spawnpoint of the passed player in MapCoordinates
	 */
	public Vector2f getSpawnPoint(int PlayerID)
	{
		return mc_spawnpoints.get(PlayerID);
	}

	/**
	 * 
	 * @param p {@link Vector2f}
	 * @return Returns if the passed {@link Vector2f} is inside the Map
	 */
	public boolean isInMap(Vector2f mc_p) 
	{
		return (mc_p.x() >= 0 && mc_p.y() >= 0 && mc_p.x() < width && mc_p.y() < height);
	}
	
}


